#include "bsdf.h"
#include "primitive.h"
#include "texture.h"

#include "material.h"

namespace csupbr
{

    Shader::Shader(const Spectrum &Kd)
        : Kd(Kd) {
    }

    Spectrum Shader::shading(Intersection &isct) const {
        // TODO: get shading normal from normal map
        Spectrum ret{intensity};
        auto bsdf = isct.primitive->material->bsdf;
        if (bsdf) {
            if ((bsdf->type & BSDF_DIFFUSE) || (bsdf->type & BSDF_GLOSSY))
                ret *= Kd;
            else if (bsdf->type & BSDF_SPECULAR)
                ret *= Ks;
            else
                return .0f; // bsdf error
        } else {
            ret *= Kd;
        }
        if (tex_d) {
            ret *= tex_d->evaluate(isct.uv);
        }
        return ret;
    }

    Spectrum SkyBoxShader::getSky(Vec3f direct, const Point2f& sample) const {
        CoordinateSystem coord(direct.normalized());
        direct = normalize(coord.toLocal(direct));
        Vec3f offset = cosineHemisphereSampling(sample);
        direct += offset * roughness;
        direct = coord.toWorld(direct);
        Spectrum ret{intensity};
        if (tex_d) {
            float phi = std::atan2(direct.z, direct.x);
            if (phi < 0.)
                phi += Pi2;
            float theta = std::acos(clamp(direct.y, -1.f, 1.f));
            Point2f uv = {phi / Pi2, theta / Pi};
            // uv += sample * roughness;
            // uv = uv - Point2f{floor(uv.u), floor(uv.v)};
            ret *= tex_d->evaluate(uv);
        } else {
            float t = -direct.y * .5f + .5f;
            ret *= (1 - t) * Kd + t * Ks;
        }
        return ret;
    }

    Material::Material(std::shared_ptr<Shader> shader, std::shared_ptr<BSDF> bsdf)
        : shader(shader), bsdf(bsdf) {
        //
    }
}